home *** CD-ROM | disk | FTP | other *** search
/ PC World Interactive 11 / PC World Interactive 11.iso / share / multimed / effect / MakeEffectMovie.c next >
Encoding:
C/C++ Source or Header  |  1998-03-12  |  19.4 KB  |  587 lines

  1. //////////
  2. //
  3. //    File:        MakeEffectMovie.c
  4. //
  5. //    Contains:    QuickTime video effect support for QuickTime movies.
  6. //                This file is used for BOTH MacOS and Windows.
  7. //
  8. //    Written by:    Tim Monroe
  9. //                Based (heavily!) on the previous MakeEffectMovie code written by Sam Bushell,
  10. //                which was based on ConvertToMovie.c from ConvertToMovie Jr. sample code.
  11. //
  12. //    Copyright:    ⌐ 1997-1998 by Apple Computer, Inc., all rights reserved.
  13. //
  14. //    Change History (most recent first):
  15. //
  16. //       <5>         03/12/98    rtm        added global flag gDoneWithDialog to fix Windows dialog box problems
  17. //       <4>         02/28/98    rtm        revised event/message handling following QTShowEffect.c
  18. //       <3>         01/08/98    rtm        sync'ed with latest code from Sam: reworked QTEffects_GetFirstVideoTrackInMovie
  19. //                                    to use GetMovieIndTrackType and fixed time scale setting code; added global flag
  20. //                                    gCopyMovieMedia and menu commands to set that flag
  21. //       <2>         11/21/97    rtm        factored out QTEffects_GetFirstVideoTrackInMovie;
  22. //                                    reworked event-loop processing: changed QTEffects_MakeEffectMovieForSources into
  23. //                                    QTEffects_DisplayDialogForSources and QTEffects_RespondToDialogSelection.
  24. //       <1>         11/06/97    rtm        first file; integrated existing code with shell framework;
  25. //                                    added endian macros where appropriate
  26. //       
  27. //    This application takes the video tracks from zero, one, or two movies 
  28. //    and creates a new movie with an effects track for them.
  29. //
  30. //    Try it out: drag two short movies of the same size onto the application icon.
  31. //  The QuickTime effects dialog will appear and you can select an effect to transition
  32. //    from the first movie to the second. Or, drag a single movie onto the application icon
  33. //    and you'll get an effects dialog for one-source effects.
  34. //
  35. //    You can also launch the application and choose the Make Effect Movie menu command in
  36. //    the Test menu. You'll get a file-opening dialog box for each movie you want to apply
  37. //    the effects to. Just hit Cancel in the first dialog box to get zero-source effects (like fire).
  38. //
  39. //////////
  40.  
  41. // TO DO:
  42. // + copy sound tracks from original movie(s); add tweening of sound tracks as effect progresses
  43.  
  44. // header files
  45. #include "MakeEffectMovie.h"
  46.  
  47. // global variables
  48. QTParameterDialog            gEffectsDialog = 0L;            // identifier for the standard parameter dialog box
  49. QTAtomContainer                gEffectSample = NULL;            // effects sample
  50. QTAtomContainer                gEffectList = NULL;
  51. PicHandle                    gPosterA = NULL;
  52. PicHandle                    gPosterB = NULL;
  53. Movie                        gSrcMovies[kMaxNumSources] = {NULL, NULL};
  54. Track                        gSrcTracks[kMaxNumSources] = {NULL, NULL};
  55. UInt16                        gSpecCount = 0;        
  56. Boolean                        gCopyMovieMedia = false;        // should we copy the movie media into the new effects movie?
  57. Boolean                        gDoneWithDialog = false;        // are we done using the effects parameters dialog box?
  58.  
  59.  
  60. //////////
  61. //
  62. // QTEffects_GetFirstVideoTrackInMovie
  63. // Return, through the theTrack parameter, the first video track in the specified movie.
  64. //
  65. // Actually, we look for the first track that has the kCharacteristicCanSendVideo characteristic,
  66. // so we can apply effects to MPEG or QD3D tracks as well.
  67. //
  68. // If no such track is found, return invalidTrack as the function result.
  69. //
  70. //////////
  71.  
  72. OSErr QTEffects_GetFirstVideoTrackInMovie (Movie theMovie, Track *theTrack)
  73. {
  74.     *theTrack = GetMovieIndTrackType(theMovie, 1, kCharacteristicCanSendVideo, movieTrackCharacteristic | movieTrackEnabledOnly);
  75.     
  76.     if (*theTrack == NULL)
  77.         return(invalidTrack);
  78.         
  79.     return(noErr);
  80. }
  81.  
  82.  
  83. //////////
  84. //
  85. // QTEffects_DisplayDialogForSources
  86. // Display the standard effects parameters dialog box for the movies passed in.
  87. //
  88. //////////
  89.  
  90. OSErr QTEffects_DisplayDialogForSources (FSSpec *theSpecList, UInt16 theSpecCount)
  91. {
  92.     OSErr                    myErr = noErr;
  93.     UInt16                    myMovieIter;
  94.     
  95.     // make sure that there aren't too many sources
  96.     if (theSpecCount > kMaxNumSources) {
  97.         myErr = paramErr;
  98.         goto bail;
  99.     }
  100.     
  101.     // assign source count to a global, so QTEffects_RespondToDialogSelection has access to it
  102.     gSpecCount = theSpecCount;        
  103.     
  104.     // open the source movies, get movies from them, and get the first video track from each movie
  105.     for (myMovieIter = 0; myMovieIter < theSpecCount; myMovieIter++) {
  106.         short    myRefNum;
  107.         
  108.         // open a movie file using the FSSpec and create a movie from that file
  109.         myErr = OpenMovieFile(&theSpecList[myMovieIter], &myRefNum, 0);
  110.         BailError(myErr);
  111.         
  112.         myErr = NewMovieFromFile(&gSrcMovies[myMovieIter], myRefNum, NULL, NULL, newMovieActive, NULL);
  113.         BailError(myErr);
  114.         
  115.         // we're done with the movie file, so close it
  116.         CloseMovieFile(myRefNum);
  117.         
  118.         // find the first video track in the source movie
  119.         myErr = QTEffects_GetFirstVideoTrackInMovie(gSrcMovies[myMovieIter], &gSrcTracks[myMovieIter]);
  120.         BailError(myErr);
  121.     }
  122.     
  123.     // ask the user to select an effect
  124.  
  125.     myErr = QTNewAtomContainer(&gEffectSample);
  126.     BailError(myErr);
  127.     
  128.     myErr = QTGetEffectsList(&gEffectList, theSpecCount, theSpecCount, 0);
  129.     BailError(myErr);
  130.     
  131.     myErr = QTCreateStandardParameterDialog(gEffectList, gEffectSample, 0, &gEffectsDialog);
  132.     BailError(myErr);
  133.     
  134.     // insert poster frames into dialog
  135.     if (gSrcTracks[0] != NULL) {
  136.         gPosterA = GetTrackPict(gSrcTracks[0], GetMoviePosterTime(gSrcMovies[0]));
  137.         if (gPosterA != NULL) {
  138.             QTParamPreviewRecord            myPreviewRecord;
  139.  
  140.             myPreviewRecord.sourcePicture = gPosterA;
  141.             myPreviewRecord.sourceID = 1;
  142.             myErr = QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &myPreviewRecord);
  143.         }
  144.     }
  145.  
  146.     if (gSrcTracks[1] != NULL) {
  147.         gPosterB = GetTrackPict(gSrcTracks[1], GetMoviePosterTime(gSrcMovies[1]));
  148.         if (gPosterB != NULL) {
  149.             QTParamPreviewRecord            myPreviewRecord;
  150.  
  151.             myPreviewRecord.sourcePicture = gPosterB;
  152.             myPreviewRecord.sourceID = 2;
  153.             myErr = QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &myPreviewRecord);
  154.         }
  155.     }
  156.     
  157.     // now, the frontmost window is the standard effects parameter dialog box;
  158.     // on the Mac, we call QTEffects_HandleEffectsDialogEvents in our main event loop
  159.     // to find and process events targeted at the effects parameter dialog box; on Windows,
  160.     // we need to use a different strategy: we install a modeless dialog callback procedure
  161.     // that is called internally by QTML
  162.  
  163. #if TARGET_OS_WIN32
  164.     gDoneWithDialog = false;
  165.     
  166.     // force the dialog box to be drawn
  167.     {
  168.         EventRecord            myEvent = {0};
  169.         
  170.         QTEffects_EffectsDialogCallback(&myEvent, FrontWindow(), 0);
  171.     }
  172.     
  173.     SetModelessDialogCallbackProc(FrontWindow(), (ModelessCallbackUPP)QTEffects_EffectsDialogCallback);
  174.     QTMLSetWindowWndProc(FrontWindow(), QTEffects_CustomDialogWndProc);
  175. #endif
  176.     
  177. bail:
  178.     return(myErr);
  179. }
  180.  
  181.  
  182. //////////
  183. //
  184. // QTEffects_RespondToDialogSelection
  185. // If theErr is codecParameterDialogConfirm, make an effects movie.
  186. // If theErr is userCanceledErr, do any necessary clean up.
  187. //
  188. //////////
  189.  
  190. void QTEffects_RespondToDialogSelection (OSErr theErr)
  191. {
  192.     short                    myMovieRefNum = 0;
  193.     short                    myResID = movieInDataForkResID;
  194.     Boolean                    myDialogWasCancelled = false;
  195.     OSType                    myEffectCode;
  196.     Fixed                    videoTrackFXWidth, videoTrackFXHeight;
  197.     TimeScale                myMovieTimeScale = 600; 
  198.     TimeValue                myEffectDuration = 0;
  199.     TimeValue                mySampleTime = 0;
  200.     StandardFileReply        myReply;
  201.     Movie                    myDestMovie = NULL;
  202.     Track                    videoTracks[kMaxNumSources] = {NULL, NULL};
  203.     Track                    videoTrackFX = NULL;
  204.     Media                    videoMediaFX = NULL;
  205.     UInt16                    myMovieIter;
  206.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  207.     OSErr                    myErr = noErr;
  208.     
  209.     // standard parameter box has been dismissed, so remember that fact
  210.     gEffectsDialog = 0L;
  211.  
  212.     myDialogWasCancelled = (theErr == userCanceledErr);
  213.  
  214.     // we're finished with the effect list and movie posters    
  215.     QTDisposeAtomContainer(gEffectList);
  216.     
  217.     if (gPosterA != NULL)
  218.         KillPicture(gPosterA);
  219.         
  220.     if (gPosterB != NULL)
  221.         KillPicture(gPosterB);
  222.     
  223.     if (myDialogWasCancelled)
  224.         goto bail;
  225.     
  226.     // add atoms naming the sources to gEffectSample
  227.     {
  228.         long    myLong;
  229.         
  230.         if (gSpecCount >= 1) {
  231.             myLong = EndianU32_NtoB(kSourceOneName);
  232.             QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myLong), &myLong, NULL);
  233.         }
  234.         
  235.         if (gSpecCount >= 2) {
  236.             myLong = EndianU32_NtoB(kSourceTwoName);
  237.             QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myLong), &myLong, NULL);
  238.         }
  239.     }
  240.     
  241.     // extract the 'what' atom to find out what kind of effect it is
  242.     {
  243.         QTAtom            myEffectAtom;
  244.         QTAtomID        myEffectAtomID;
  245.         long            myEffectCodeSize;
  246.         Ptr                myEffectCodePtr;
  247.  
  248.         myEffectAtom = QTFindChildByIndex(gEffectSample, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, &myEffectAtomID);
  249.         
  250.         myErr = QTLockContainer(gEffectSample);
  251.         BailError(myErr);
  252.  
  253.         myErr = QTGetAtomDataPtr(gEffectSample, myEffectAtom, &myEffectCodeSize, &myEffectCodePtr);
  254.         BailError(myErr);
  255.  
  256.         if (myEffectCodeSize != sizeof(OSType)) {
  257.             myErr = paramErr;
  258.             goto bail;
  259.         }
  260.         
  261.         myEffectCode = *(OSType *)myEffectCodePtr;        // "tsk"
  262.         myEffectCode = EndianU32_BtoN(myEffectCode);    // because the data is read from an atom container
  263.         
  264.         myErr = QTUnlockContainer(gEffectSample);
  265.         BailError(myErr);
  266.     }
  267.  
  268.     // ask the user for the name of the new movie file
  269.     StandardPutFile("\pSave effect movie file as:", "\pUntitled.mov", &myReply);
  270.     if (!myReply.sfGood)
  271.         goto bail;                // deal with user cancelling
  272.  
  273.     // create a movie file for the destination movie
  274.     myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), 0, myFlags, &myMovieRefNum, &myDestMovie);
  275.     BailError(myErr);
  276.     
  277.     // copy the user data and settings from the source to the destination movie;
  278.     // these settings include information like user data
  279.     if (gSpecCount > 0)
  280.         CopyMovieSettings(gSrcMovies[0], myDestMovie);
  281.     
  282.     // convert all the movies to have a common time scale;
  283.     // we pick the largest time scale out of all the source movies, with a minimum of 600
  284.     myMovieTimeScale = 600;
  285.     for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++) {
  286.         if (myMovieTimeScale < GetMovieTimeScale(gSrcMovies[myMovieIter]))
  287.             myMovieTimeScale = GetMovieTimeScale(gSrcMovies[myMovieIter]);
  288.     }
  289.     
  290.     for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++) {
  291.         if (myMovieTimeScale != GetMovieTimeScale(gSrcMovies[myMovieIter]))
  292.             SetMovieTimeScale(gSrcMovies[myMovieIter], myMovieTimeScale);
  293.     }
  294.     
  295.     // the effect duration is the minimum of the length of the tracks
  296.     if (gSpecCount == 0)
  297.         myEffectDuration = myMovieTimeScale * 10;
  298.     else
  299.         myEffectDuration = GetTrackDuration(gSrcTracks[0]);
  300.     
  301.     for (myMovieIter = 1; myMovieIter < gSpecCount; myMovieIter++) {
  302.         if (myEffectDuration > GetTrackDuration(gSrcTracks[myMovieIter]))
  303.             myEffectDuration = GetTrackDuration(gSrcTracks[myMovieIter]);
  304.     }
  305.     
  306.     // default size when there are no video tracks
  307.     videoTrackFXWidth = 160L << 16;
  308.     videoTrackFXHeight = 120L << 16;
  309.  
  310.     for (myMovieIter = 0; myMovieIter < kMaxNumSources; myMovieIter++) {
  311.         videoTracks[myMovieIter] = NULL;
  312.     }
  313.     
  314.     // make the video tracks
  315.     for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++) {
  316.         Fixed    mySrcTrackWidth, mySrcTrackHeight;
  317.         OSType    mySrcMediaType = 0;
  318.         Media    videoMedia;
  319.  
  320.         // videoTracks[n] is a clone of gSrcTracks[n]
  321.         GetTrackDimensions(gSrcTracks[myMovieIter], &mySrcTrackWidth, &mySrcTrackHeight);
  322.         
  323.         if ((myMovieIter == 0) || (videoTrackFXWidth < mySrcTrackWidth))
  324.                 videoTrackFXWidth = mySrcTrackWidth;
  325.                 
  326.         if ((myMovieIter == 0) || (videoTrackFXHeight < mySrcTrackHeight))
  327.                 videoTrackFXHeight = mySrcTrackHeight;
  328.         
  329.         GetMediaHandlerDescription(GetTrackMedia(gSrcTracks[myMovieIter]), &mySrcMediaType, NULL, NULL);
  330.  
  331.         videoTracks[myMovieIter] = NewMovieTrack(myDestMovie, mySrcTrackWidth, mySrcTrackHeight, 0);
  332.         BailNil(videoTracks[myMovieIter]);
  333.         videoMedia = NewTrackMedia(videoTracks[myMovieIter], mySrcMediaType, myMovieTimeScale, NULL, 0);
  334.         BailNil(videoMedia);
  335.         
  336.         if (gCopyMovieMedia) {
  337.             myErr = BeginMediaEdits(videoMedia);
  338.             BailError(myErr);
  339.         }
  340.  
  341.         myErr = CopyTrackSettings(gSrcTracks[myMovieIter], videoTracks[myMovieIter]);
  342.         BailError(myErr);
  343.         myErr = InsertTrackSegment(gSrcTracks[myMovieIter], videoTracks[myMovieIter], 0, myEffectDuration, 0);
  344.         BailError(myErr);
  345.         
  346.         if (gCopyMovieMedia) {
  347.             myErr = EndMediaEdits(videoMedia);
  348.             BailError(myErr);
  349.         }
  350.     }
  351.     
  352.     {
  353.         // videoTrackFX is the special track that implements the effect
  354.         videoTrackFX = NewMovieTrack(myDestMovie, videoTrackFXWidth, videoTrackFXHeight, 0);
  355.         BailNil(videoTrackFX);
  356.         videoMediaFX = NewTrackMedia(videoTrackFX, VideoMediaType, myMovieTimeScale, NULL, 0);
  357.         BailNil(videoMediaFX);
  358.     }
  359.  
  360.     {
  361.         ImageDescriptionHandle    myDesc = NULL;
  362.  
  363.         // create a new sample description
  364.         myDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  365.         BailNil(myDesc);
  366.         
  367.         // fill in the fields of the sample description
  368.         (**myDesc).idSize = sizeof(ImageDescription);
  369.         (**myDesc).cType = myEffectCode;
  370.         (**myDesc).vendor = kAppleManufacturer;
  371.         (**myDesc).temporalQuality = codecNormalQuality;
  372.         (**myDesc).spatialQuality = codecNormalQuality;
  373.         (**myDesc).width = videoTrackFXWidth >> 16;
  374.         (**myDesc).height = videoTrackFXHeight >> 16;
  375.         (**myDesc).hRes = 72L << 16;
  376.         (**myDesc).vRes = 72L << 16;
  377.         (**myDesc).dataSize = 0L;
  378.         (**myDesc).frameCount = 1;
  379.         (**myDesc).depth = 0;
  380.         (**myDesc).clutID = -1;
  381.         
  382.         // add effects sample to movie
  383.         myErr = BeginMediaEdits(videoMediaFX);
  384.         BailError(myErr);
  385.  
  386.         myErr = AddMediaSample(videoMediaFX, gEffectSample, 0, GetHandleSize(gEffectSample), myEffectDuration, (SampleDescriptionHandle)myDesc, 1, 0, &mySampleTime);
  387.         BailError(myErr);
  388.  
  389.         myErr = EndMediaEdits(videoMediaFX);
  390.         BailError(myErr);
  391.  
  392.         QTDisposeAtomContainer(gEffectSample);
  393.         DisposeHandle((Handle)myDesc);
  394.     }
  395.  
  396.     myErr = InsertMediaIntoTrack(videoTrackFX, 0, mySampleTime, myEffectDuration, fixed1);
  397.     BailError(myErr);
  398.  
  399.     // create and add the input map
  400.     {
  401.         long                myRefIndex1, myRefIndex2;
  402.         QTAtomContainer        myInputMap;
  403.         QTAtom                myInputAtom;
  404.         OSType                myInputType;
  405.         long                myLong;
  406.  
  407.         QTNewAtomContainer(&myInputMap);
  408.  
  409.         // first input
  410.         if (videoTracks[0]) {
  411.             AddTrackReference(videoTrackFX, videoTracks[0], kTrackModifierReference, &myRefIndex1);
  412.             QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex1, 0, 0, NULL, &myInputAtom);
  413.     
  414.             myInputType = EndianU32_NtoB(kTrackModifierTypeImage);
  415.             QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL);
  416.     
  417.             myLong = EndianU32_NtoB(kSourceOneName);
  418.             QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL);
  419.         }
  420.  
  421.         // second input
  422.         if (videoTracks[1]) {
  423.             AddTrackReference(videoTrackFX, videoTracks[1], kTrackModifierReference, &myRefIndex2);
  424.             QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex2, 0, 0, NULL, &myInputAtom);
  425.     
  426.             myInputType = EndianU32_NtoB(kTrackModifierTypeImage);
  427.             QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL);
  428.     
  429.             myLong = EndianU32_NtoB(kSourceTwoName);
  430.             QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL);
  431.         }
  432.  
  433.         // set that map
  434.         if (gSpecCount > 0)
  435.             SetMediaInputMap(GetTrackMedia(videoTrackFX), myInputMap);
  436.         
  437.         QTDisposeAtomContainer(myInputMap);
  438.     }
  439.  
  440.     myErr = AddMovieResource(myDestMovie, myMovieRefNum, &myResID, "\pMovie 1");
  441.     BailError(myErr);
  442.     
  443.     CloseMovieFile(myMovieRefNum);
  444.     
  445.     for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++)
  446.         DisposeMovie(gSrcMovies[myMovieIter]);
  447.         
  448.     DisposeMovie(myDestMovie);
  449.     
  450. bail:
  451.     return;
  452. }
  453.  
  454.  
  455. #if TARGET_OS_WIN32
  456. //////////
  457. //
  458. // QTEffects_EffectsDialogCallback
  459. // This function is called by QTML when it processes events for the standard or custom effects parameter dialog box.
  460. // 
  461. //////////
  462.  
  463. static void QTEffects_EffectsDialogCallback (EventRecord *theEvent, DialogRef theDialog, DialogItemIndex theItemHit)
  464. {
  465.     QTParamDialogEventRecord    myRecord;
  466.  
  467.     myRecord.theEvent = theEvent;
  468.     myRecord.whichDialog = theDialog;
  469.     myRecord.itemHit = theItemHit;
  470.  
  471.     if (gEffectsDialog != 0L) {
  472.         QTStandardParameterDialogDoAction(gEffectsDialog, pdActionModelessCallback, &myRecord);
  473.     
  474.         // see if the event is meant for the effects parameter dialog box
  475.         QTEffects_HandleEffectsDialogEvents(theEvent, theItemHit);
  476.     }
  477. }
  478.  
  479.  
  480. //////////
  481. //
  482. // QTEffects_CustomDialogWndProc
  483. // Handle messages for the custom effects parameters dialog box.
  484. // 
  485. //////////
  486.  
  487. LRESULT CALLBACK QTEffects_CustomDialogWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
  488. {
  489.     EventRecord            myEvent = {0};
  490.  
  491.     if (!gDoneWithDialog && (theMessage == 0x7FFF))
  492.         QTEffects_EffectsDialogCallback(&myEvent, GetNativeWindowPort(theWnd), 0);
  493.  
  494.     return(DefWindowProc(theWnd, theMessage, wParam, lParam));
  495. }
  496. #endif
  497.  
  498.  
  499. //////////
  500. //
  501. // QTEffects_HandleEffectsDialogEvents
  502. // Process events that might be targeted at the standard effects parameter dialog box.
  503. // Return true if the event was completely handled.
  504. // 
  505. //////////
  506.  
  507. Boolean QTEffects_HandleEffectsDialogEvents (EventRecord *theEvent, DialogItemIndex theItemHit)
  508. {
  509.     Boolean            isHandled = false;
  510.     OSErr            myErr = noErr;
  511.     
  512.     // pass the event to the standard effects parameter dialog box handler
  513.     myErr = QTIsStandardParameterDialogEvent(theEvent, gEffectsDialog);
  514.     
  515.     // the result from QTIsStandardParameterDialogEvent tells us how to respond next
  516.     switch (myErr) {
  517.         
  518.         case codecParameterDialogConfirm:
  519.         case userCanceledErr:
  520.             // the user clicked the OK or Cancel button; dismiss the dialog box and respond accordingly
  521.             gDoneWithDialog = true;
  522.             QTStandardParameterDialogDoAction(gEffectsDialog, pdActionConfirmDialog, NULL);
  523.             QTDismissStandardParameterDialog(gEffectsDialog);
  524.             gEffectsDialog = 0L;
  525.             QTEffects_RespondToDialogSelection(myErr);
  526.             isHandled = true;
  527.             break;
  528.             
  529.         case noErr:
  530.             // the event was completely handled by QTIsStandardParameterDialogEvent
  531.             isHandled = true;
  532.             break;
  533.             
  534.         case featureUnsupported:
  535.             // the event was not handled by QTIsStandardParameterDialogEvent;
  536.             // let the event be processed normally
  537.             isHandled = false;
  538.             break;
  539.             
  540.         default:
  541.             // the event was not handled by QTIsStandardParameterDialogEvent;
  542.             // do not let the event be processed normally
  543.             isHandled = true;
  544.             break;
  545.     }
  546.  
  547.     return(isHandled);
  548. }
  549.  
  550.  
  551. //////////
  552. //
  553. // QTEffects_PromptUserForFilesAndMakeEffect
  554. // Let the user select some movies, then apply the effect to them.
  555. //
  556. // If the user cancels the first file-open dialog box, there are zero sources.
  557. // If the user cancels the second file-open dialog box, there is one source.
  558. // 
  559. //////////
  560.  
  561. void QTEffects_PromptUserForFilesAndMakeEffect (void)
  562. {
  563.     FSSpec        mySpecs[kMaxNumSources];
  564.     int            mySpecCount;
  565.     
  566.     // ask for up to kMaxNumSources movie files;
  567.     // accept early cancels; they just mean there are fewer input movies
  568.     mySpecCount = 0;
  569.     while (mySpecCount < kMaxNumSources) {
  570.         SFTypeList                myTypeList;
  571.         StandardFileReply        myReply;
  572.  
  573.         myTypeList[0] = MovieFileType;
  574.  
  575.         StandardGetFilePreview(NULL, 1, myTypeList, &myReply);
  576.         if (!myReply.sfGood)
  577.             break;    // the user doesn't want any more source movies
  578.     
  579.         // save the FSSpec from the reply information
  580.         mySpecs[mySpecCount] = myReply.sfFile;
  581.         
  582.         mySpecCount++;
  583.     }
  584.     
  585.     QTEffects_DisplayDialogForSources(mySpecs, mySpecCount);
  586. }
  587.